//
//  IBCDownLoadManager.m
//  IBCDownLoadManager
//
//  Created by yonas on 2018/5/23.
//  Copyright © 2018年 yonas. All rights reserved.
//

#import "PYNDownLoadManager.h"
#import "CheckUtile.h"
#import "PYNDownLoadError.h"
#import <AFNetworking/AFHTTPSessionManager.h>
#import <pthread.h>

#ifdef DEBUG
#define IBCDownloadLog(...) NSLog(__VA_ARGS__)
#else
#define IBCDownloadLog(...)
#endif

static PYNDownLoadManager *ibcmanager = nil;
static pthread_mutex_t _lock;
static inline void LOCK(){
    __unused int result = pthread_mutex_lock(&_lock);
}

static inline void UNLOCK(){
    __unused int result = pthread_mutex_unlock(&_lock);
}

static inline dispatch_queue_t IBCDownloadManagerGetReleaseQueue() {
    return dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
}

@interface PYNDownLoadManager(){
    @package
    CFMutableDictionaryRef _dictionaryRef;//用来做url和任务的映射。
}
@end


@implementation PYNDownLoadManager

#pragma mark - 单例
+ (instancetype)sharedManager{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        ibcmanager = [[self alloc]init];
    });
    return ibcmanager;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone{
    if (!ibcmanager)
    {
        ibcmanager = [super allocWithZone:zone];
    }
    return ibcmanager;
}
- (id)copyWithZone:(NSZone *)zone{
    return [PYNDownLoadManager sharedManager];
}
- (instancetype)init{
    if (self = [super init]){
        _dictionaryRef = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
        pthread_mutex_init(&_lock, NULL);
    }
    return self;
}

#pragma mark - public
- (void)downLoadWithUrl:(NSURL *)url{
    self.delegate = nil;
    [self downLoadWithUrl:url targetPath:nil progressHandler:nil completionHandler:nil];
}

- (void)downLoadWithUrl:(NSURL *)url targetPath:(NSString *)targetPath delegate:(id<PYNDownloadProtocol>)delegate{
    
    self.delegate = delegate;
    [self downLoadWithUrl:url targetPath:targetPath progressHandler:nil completionHandler:nil];
}

- (void)downLoadWithUrl:(NSURL *)url targetPath:(NSString *)targetPath completionHandler:(void (^)(NSError *, NSString *))completionHandler{
    [self downLoadWithUrl:url targetPath:targetPath progressHandler:nil completionHandler:completionHandler];
    
}

- (void)downLoadWithUrl:(NSURL *)url targetPath:(NSString *)targetPath progressHandler:(void (^)(NSProgress *))progressHandler completionHandler:(void (^)(NSError *, NSString *))completionHandler{
    //-1.首先判断是否url对应的target存在
    NSURLSessionTask *task = [self _getDownloadTaskWithUrl:url];
    if (task){
        return;
    }
    
    //0.对路径进行校验
    if (![self _checkFilePathWithFilePath:targetPath]){
        NSError *faluleError = [PYNDownLoadError errorByTargetPathNotFound];
        if (self.delegate&&[self.delegate respondsToSelector:@selector(ibcDownloadFailureWithUrl:destinatePath:error:)]){
            [self.delegate ibcDownloadFailureWithUrl:url destinatePath:targetPath error:faluleError];
        }else{
            IBCDownloadLog(@"error = %@",faluleError);
        }
        return;
    }
    
    //1.对Url校验，正则
    if (url == nil||url.absoluteString.length == 0||![CheckUtile checkUrl:url.absoluteString]){
        NSError *faluleError = [PYNDownLoadError errorByUrlNotCorrect];
        if (self.delegate&&[self.delegate respondsToSelector:@selector(ibcDownloadFailureWithUrl:destinatePath:error:)]){
            [self.delegate ibcDownloadFailureWithUrl:url destinatePath:targetPath error:faluleError];
        }else{
            IBCDownloadLog(@"error = %@",[NSString stringWithFormat:@"IBCDownloadError:错误的url格式:%@",url]);
        }
        return;
    }
    //1.1设置超时
    NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
    config.timeoutIntervalForRequest = 8.f;
    AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc]initWithSessionConfiguration:config];
    //2.开始下载
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    __weak typeof(self) weakSelf = self;
    NSURLSessionDownloadTask *downLoadTask = [manager downloadTaskWithRequest:request progress:^(NSProgress * _Nonnull downloadProgress) {
        dispatch_async(dispatch_get_main_queue(), ^{
            if (weakSelf.delegate&&[weakSelf.delegate respondsToSelector:@selector(ibcDownloadDidStartWithUrl:destinatePath:progress:)]){
                [weakSelf.delegate ibcDownloadDidStartWithUrl:url destinatePath:targetPath progress:downloadProgress];
            }else if (progressHandler){
                progressHandler(downloadProgress);
            }else{
                self->_downLoadProgress = downloadProgress;
            }
        });
    } destination:^NSURL * _Nonnull(NSURL * _Nonnull path, NSURLResponse * _Nonnull response) {
        if (targetPath||targetPath.length!=0){
            return [NSURL fileURLWithPath:targetPath];
        }else{
            //默认路径下载到cache目录，名字为文件名。
            NSString *fullPath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory,
                                                                       NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:response.suggestedFilename];
            return [NSURL fileURLWithPath:fullPath];
        }
    } completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
        //下载完成后的处理
        [weakSelf _downloadEndWithResponse:response filePath:filePath?[NSString stringWithUTF8String:filePath.fileSystemRepresentation]:nil error:error url:response.URL completionHandler:completionHandler];
        //丢弃task。
        LOCK();
        CFDictionaryRemoveValue(self->_dictionaryRef, (__bridge const void *)(response.URL));
        UNLOCK();
    }];
    
    [downLoadTask resume];
    if (self.delegate&&[self.delegate respondsToSelector:@selector(ibcDownloadWillStartWithUrl:destinatePath:)]){
        [self.delegate ibcDownloadWillStartWithUrl:url destinatePath:targetPath];
    }
    LOCK();
    CFDictionaryAddValue(_dictionaryRef, (__bridge const void *)url,(__bridge const void *)downLoadTask);
    UNLOCK();
}
- (void)setDelegate:(id<PYNDownloadProtocol>)delegate{
    LOCK();
    _delegate = delegate;
    UNLOCK();
}

- (void)pasueTaskWithUrl:(NSURL *)url{
    LOCK();
    [[self _getDownloadTaskWithUrl:url] suspend];
    UNLOCK();
}

- (void)cancelTaskWithUrl:(NSURL *)url{
    LOCK();
    NSURLSessionTask *downloadTask = [self _getDownloadTaskWithUrl:url];
    if (downloadTask){
        dispatch_async(IBCDownloadManagerGetReleaseQueue(), ^{
            [downloadTask cancel];
            CFDictionaryRemoveValue(self->_dictionaryRef, (__bridge const void *)url);
        });
    }
    UNLOCK();
}

- (void)restartTaskWithUrl:(NSURL *)url{
    LOCK();
    NSURLSessionTask *task = [self _getDownloadTaskWithUrl:url];
    UNLOCK();
    if (task&&task.state == NSURLSessionTaskStateSuspended){
        [task resume];
    }else{
        [self downLoadWithUrl:url];
    }
}

#pragma mark - private
- (NSURLSessionTask *)_getDownloadTaskWithUrl:(NSURL *)url{
    if (CFDictionaryContainsKey(_dictionaryRef, (__bridge const void *)url)){
        NSURLSessionTask *downLoadTask = CFDictionaryGetValue(_dictionaryRef,(__bridge const void *)url);
        return downLoadTask;
    }
    IBCDownloadLog(@"%@",[PYNDownLoadError errorByNotFoundDownloadTaskWithUrl]);
    return nil;
}

//验证文件完整性
- (BOOL)_checkFileWhetherCompleteWithFilePath:(NSString *)filePath originalMD5:(NSString *)originalMD5{
    BOOL complete = [CheckUtile checkMD5WithFileWithPath:filePath originalMD5:originalMD5];
    if (complete){
        return YES;
    }else{
        //错误处理,这里默认删除校验不通过的文件
        [self _deleteFileIfNotCompleteWithFilePath:filePath];
        return NO;
    }
}

//删除校验失败的文件
- (void)_deleteFileIfNotCompleteWithFilePath:(NSString *)filePath{
    NSFileManager *manager = [NSFileManager defaultManager];
    NSError *error = nil;
    [manager removeItemAtPath:filePath error:&error];
}

//文件路径校验
- (BOOL)_checkFilePathWithFilePath:(NSString *)filePath{
    //filepath有值才校验，校验文件夹是否存在，校验文件名是否写了。
    if (filePath!=nil&&filePath.length!=0){
        if ([[filePath copy] stringByDeletingLastPathComponent].length == 0){
            return NO;
        }else if (![CheckUtile checkFileExistsWithPath:filePath]){
            return NO;
        }else{
            
        }
    }
    return YES;
}
//下载完成后的处理
- (void)_downloadEndWithResponse:(NSURLResponse *)response filePath:(NSString *)filePath error :(NSError *)error url:(NSURL *)url completionHandler:(void (^)(NSError *, NSString *))completionHandler{
    //1.如果本身有错误，就不用校验完整性
    if (!error){
        //2.只有本身下载没有错误才可以校验文件完整性。
        NSDictionary *headerDict  = [(NSHTTPURLResponse *)response allHeaderFields];
        NSString *originalMd5 = headerDict[@"Content-MD5"];
        if (![self _checkFileWhetherCompleteWithFilePath:filePath originalMD5:[originalMd5 uppercaseString]]){
            error = [PYNDownLoadError errorByFileNotComplete];
        }
    }
    
    if (self.delegate&&[self.delegate respondsToSelector:@selector(ibcDownloadEndWithUrl:destinatePath:error:)]){
        
        [self.delegate ibcDownloadEndWithUrl:url destinatePath:filePath error:error];
        
    }else if (completionHandler){
        if (filePath == nil){
            completionHandler(error,nil);
        }else{
            completionHandler(error,filePath);
        }
    }
}

- (void)dealloc{
    free(_dictionaryRef);
    pthread_mutex_destroy(&_lock);
}
@end
